using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;
using System.Collections;
using FullMotion.LiveForSpeed.InSim;
using System.Xml;
//using log4net;

namespace FGHandler
{
    

    class LFSHandler
    {
        private Hashtable _hashtable;
        public InSimHandler _handler;

        
        public Dictionary<string, lfsteam> _teamlist = new Dictionary<string, lfsteam>();

        

        public delegate void PlayerJoinedEventHandler(LFSHandler sender, lfsplayer player);
        public event PlayerJoinedEventHandler PlayerJoined;

        public delegate void PlayerSplitTimeEventHandler(LFSHandler sender, lfsplayer player);
        public event PlayerSplitTimeEventHandler PlayerSplitTime;

        /*private void OnPlayerJoined(string username)
        {
            PlayerJoined(this, "string");
        }*/
        //private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);



        public delegate void RaceTrackConnectionLeave(LFSHandler sender, lfsplayer player);
        public event RaceTrackConnectionLeave FGRaceTrackConnectionLeave;

        public FullMotion.LiveForSpeed.InSim.Configuration Configuration; // For configure stuff. Implementation Later
        public LFSHandler()
        {
            try
            {
                _hashtable = new Hashtable();
                _hashtable = Hashtable.Synchronized(_hashtable);

                _handler = new InSimHandler();
                //_handler.Configuration.LFSHost = "82.141.123.28";
                //_handler.Configuration.LFSHost = "127.0.0.1";
                _handler.Configuration.LFSHost = "192.168.1.90";
                _handler.Configuration.LFSHostPort = 30000;
                _handler.Configuration.AdminPass = "fgclarkson";
                _handler.Configuration.MultiCarTracking = true;
                _handler.Configuration.KeepMessageColors = false;
                _handler.Configuration.ProgramName = "LFS WebStat";
                _handler.Configuration.UseTCP = true;
                _handler.Configuration.TrackingInterval = 1000;

                this.HandleEvents();
                //this.ReadTeamList();
                //log.Debug("InSimHandler created");
                //log.Fatal(this);
                
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

       


        public void Connect()
        {
            try
            {
                _handler.Initialize();
                _handler.RequestConnectionInfo();
                _handler.RequestPlayerInfo();
                //_handler.RequestRaceTrackingMultiCarInfo();
                _handler.RequestPing();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
                //Console.WriteLine(ex.Message);
            }
        }

        private void HandleEvents()
        {
            _handler.LFSMessage += new FullMotion.LiveForSpeed.InSim.EventHandlers.MessageEventHandler(_handler_LFSMessage);
            // Server joining/leaving stuff
            _handler.RaceTrackConnection += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackNewConnectionHandler(_handler_RaceTrackConnection);
            _handler.RaceTrackConnectionLeave += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackConnectionLeaveHandler(_handler_RaceTrackConnectionLeave);

            // Race joining/leaving stuff
            _handler.RaceTrackPlayer += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerHandler(_handler_RaceTrackPlayer);
            _handler.RaceTrackPlayerLeave += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerLeaveHandler(_handler_RaceTrackPlayerLeave);

            // Lap and splittime stuff
            _handler.RaceTrackPlayerLap += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerLapHandler(_handler_RaceTrackPlayerLap);
            _handler.RaceTrackPlayerSplitTime += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerSplitTimeHandler(_handler_RaceTrackPlayerSplitTime);

            // Current Racer information.
            _handler.RaceTrackMultiCarInfo += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackMultiCarInfoHandler(_handler_RaceTrackMultiCarInfo);
            _handler.RaceTrackPlayerPenalty += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPenaltyHandler(_handler_RaceTrackPlayerPenalty);
            _handler.RaceTrackPlayerPitStopBegin += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitStopBeginHandler(_handler_RaceTrackPlayerPitStopBegin);
            _handler.RaceTrackPlayerPitStopFinish += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitStopFinishHandler(_handler_RaceTrackPlayerPitStopFinish);

            _handler.RaceTrackRaceStart += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackRaceStartHandler(_handler_RaceTrackRaceStart);
            _handler.RaceTrackCarTakeover += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackCarTakeoverHandler(_handler_RaceTrackCarTakeover);

            _handler.Ping += new FullMotion.LiveForSpeed.InSim.EventHandlers.PingEventHandler(_handler_Ping);

            _handler.LFSMultiplayer += new FullMotion.LiveForSpeed.InSim.EventHandlers.MultiplayerEventHandler(_handler_LFSMultiplayer);
            _handler.StateChanged += new FullMotion.LiveForSpeed.InSim.EventHandlers.StateChangeHandler(_handler_StateChanged);

            _handler.LFSState += new FullMotion.LiveForSpeed.InSim.EventHandlers.StateEventHandler(_handler_LFSState);
        }

        void _handler_LFSState(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.State e)
        {
            //
        }

        void _handler_Ping(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.Ping e)
        {
            Console.WriteLine("PING " + e.RequestId );
        }

        void _handler_StateChanged(InSimHandler sender, StateChangeEventArgs e)
        {
            Console.WriteLine("previous state: {0}, current state: {1}", e.PreviousState, e.CurrentState);
        }

        void _handler_LFSMultiplayer(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.Multiplayer e)
        {
            Console.WriteLine("_handler_LFSMultiplayer");
            _handler.RequestConnectionInfo();            
        }


        void _handler_RaceTrackCarTakeover(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackCarTakeover e)
        {
            lfsplayer newPlayer = (lfsplayer)_hashtable[GetUsernameFromConnectionID(e.NewConnectionId)];
            lfsplayer oldPlayer = (lfsplayer)_hashtable[GetUsernameFromConnectionID(e.OldConnectionId)];

            /* ConnectionID vs Playerid
             * 
             * 
             */
            newPlayer.ConnectionID = oldPlayer.ConnectionID;
            newPlayer.PlayerID = oldPlayer.PlayerID;
            oldPlayer.ConnectionID = newPlayer.ConnectionID;
            oldPlayer.PlayerID = newPlayer.PlayerID;

            oldPlayer.TeamCurrent = false;
            newPlayer.TeamCurrent = true;

            oldPlayer.Spectating = true;
            newPlayer.Spectating = false;
            

        }

        private FullMotion.LiveForSpeed.InSim.Events.RaceTrackRaceStart _RaceTrackRaceStart;
        void _handler_RaceTrackRaceStart(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackRaceStart e)
        {
            // The race Starts!
            Console.WriteLine("Track {0}: {1},{2},{3},{4}", e.ShortTrackName, e.FinishNodeIndex, e.Split1NodeIndex, e.Split2NodeIndex, e.Split3NodeIndex);
            this._RaceTrackRaceStart = e;
        }

        void _handler_RaceTrackPlayerLeave(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerLeave e)
        {
            lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromPlayerID(e.PlayerId)];

            if (player != null)
            {
                if (player.GotTeam)
                {
                    // This player is no longer the active racer.
                    player.TeamCurrent = false;
                }
                player.Spectating = true;
            }
            else
                throw new Exception("_handler_RaceTrackPlayerLeave. No Player with ID " + e.PlayerId);

        }

        void _handler_RaceTrackPlayerPitStopFinish(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerPitStopFinish e)
        {
            // Finished
           lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromPlayerID(e.PlayerId)];
           if (player != null)
           {
               // Calculate the total pitstop time.
               player.PitStopTime = TimeSpan.FromTicks(DateTime.Now.Ticks).Subtract(player.PitStopStarted);
           }
            
        }

        void _handler_RaceTrackPlayerPitStopBegin(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerPitStopBegin e)
        {
            lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromPlayerID(e.PlayerId)];
            if (player != null)
            {
                player.RaceTrackPlayerPitStop(e);                
            }
        }

        void _handler_RaceTrackPlayerPenalty(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerPenalty e)
        {

            lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromPlayerID(e.PlayerId)];
            if (player != null)
            {
                player.RaceTrackPlayerPenalty(e);

                //_teamlist[player.BelongsToTeam].penalty = e.NewPenalty;
            } 
            
            Console.WriteLine("penalty: {0}/{1} ({2})", e.NewPenalty, e.OldPenalty, e.Reason);
        }

        void _handler_RaceTrackMultiCarInfo(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackMultiCarInfo e)
        {
            for (int i = 0; i < System.Math.Min(8, e.CarInfo.Length); i++)
            {
                lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromPlayerID(e.CarInfo[i].PlayerId)];
                if (player != null)
                {
                    player.CarInfo(e.CarInfo[i]);
                    //Console.WriteLine("MCI");
                }
            }
        }

        void _handler_RaceTrackPlayerSplitTime(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerSplitTime e)
        {
            lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromPlayerID(e.PlayerId)];
            if (player != null)
            {
                player.RaceTrackPlayerSplitTime(e);
                // Trigger the Event for updating the GUI.
                this.PlayerSplitTime(this, player);
            }

            
            
        }

        void _handler_RaceTrackPlayerLap(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerLap e)
        {

            lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromPlayerID(e.PlayerId)];
            if (player != null)
            {
                player.RaceTrackPlayerLap(e);

                // Team Stuff (Event Trigger?)
                if (player.GotTeam)
                {
                    //Console.WriteLine("Team Laps " + _teamlist[player.Teamname].GetTotalLaps + " " + FormatTime(_teamlist[player.Teamname].AvgLapTime));
                    //Console.WriteLine("Team Disconnects: " + _teamlist[player.Teamname].Disconnects);
                }
                
            }
            
        }

        /// <summary>
        /// Searches the corresponding username in the Hashtable.
        /// </summary>
        /// <param name="connectionid">The connectionid of the racer</param>
        /// <returns>Return: The racer his username</returns>
        public string GetUsernameFromConnectionID(byte connectionid)
        {
            try
            {
                foreach (DictionaryEntry Entry in this._hashtable)
                {
                    lfsplayer player = (lfsplayer)Entry.Value;
                    if (player.ConnectionID == connectionid)
                        return player.Username;
                }
                throw new Exception("No Player found with ConnID: " + connectionid);
            }
            catch (Exception ex)
            {
                throw new Exception("No Player found with ConnID: " + connectionid);
            }
        }

        /// <summary>
        /// Searches the corresponding username in the Hashtable.
        /// </summary>
        /// <param name="connectionid">The playerid of the racer</param>
        /// <returns>Return: The racer his username</returns>
        public string GetUsernameFromPlayerID(byte playerid)
        {
            try
            {
                foreach (DictionaryEntry Entry in this._hashtable)
                {
                    lfsplayer player = (lfsplayer)Entry.Value;
                    if (player.PlayerID == playerid)
                        return player.Username;
                }
                throw new Exception("No Player found with playerID: " + playerid);
            }
            catch (Exception ex)
            {
                throw new Exception("No Player found with playerID: " + playerid);
            }
        }

        void _handler_RaceTrackPlayer(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayer e)
        {
            lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromConnectionID(e.ConnectionId)];
            if (player != null)
                player.RaceTrackPlayer(e);
            else
                throw new Exception("_handler_RaceTrackPlayer. No Player with ID " + e.PlayerId);

            
            // Player belongs to an team!
            if (player.GotTeam)
            {
                player.TeamCurrent = true;
                Console.WriteLine("Player is currently active Team racer");
            }
            
        }

        void _handler_RaceTrackConnectionLeave(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackConnectionLeave e)
        {            
            if (e.TotalConnections != _hashtable.Count)
                Console.WriteLine("Total Connections doesnt match the number of entries in the Hashtable");

            lfsplayer player = (lfsplayer)_hashtable[GetUsernameFromConnectionID(e.ConnectionId)];


            
            if (player.GotTeam)
            {

                // If player belongs to a team...
                if ((e.Reason == FullMotion.LiveForSpeed.InSim.Enums.LeaveReason.LostConnection))
                {
                    _teamlist[player.Teamname].Disconnects++;
                    Console.WriteLine("Team " + player.Teamname + "  got -1 lap!");
                }
            }

            if (player != null)
            {
                FGRaceTrackConnectionLeave(this, player);
                _hashtable.Remove(player.Username);
            }
            else
                throw new Exception("No player found with ConnID: " + e.ConnectionId);
            
        }

        void _handler_RaceTrackConnection(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackConnection e)
        {
            if (e.ConnectionId > 0) // 0 == LFS Server.
            {
                lfsplayer player = new lfsplayer();
                player.RaceTrackConnection(e);

                if (!_hashtable.ContainsKey(player.Username))
                {
                    foreach (KeyValuePair<string, lfsteam> test in _teamlist)
	                {
                        lfsteam testteam = test.Value;
                        // First check for usernames. Read by XML first.
                        if (testteam._teamUsernames.Contains(e.UserName))
                        {
                            // If the user belongsto the team, Add this object. So we can track the player and team moves.
                            player.GotTeam = true;
                            player.Teamname = testteam.TeamName;
                            testteam.AddPlayer(player);
                        }
                        
	                }
                    // Trigger the Join Event for GUI stuff :)
                    PlayerJoined(this, player);
                    this._hashtable.Add(player.Username, player);
                }
            }
        }

        private void _handler_LFSMessage(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.Message e)
        {
            //Console.WriteLine(e.MessageText);            
        }

        public static string FormatTime(TimeSpan t)
        {
            int secFraction = t.Milliseconds / 10;
            return string.Format("{0}:{1:00}.{2:00}", t.Minutes, t.Seconds, secFraction);
        }

        private void ReadTeamList()
        {

            try
            {
                string filename = System.Reflection.Assembly.GetExecutingAssembly().Location + "/teamlist.xml";
                XmlTextReader reader = new XmlTextReader(filename);
                XmlDocument doc = new XmlDocument();
                doc.Load(reader);
                XmlNodeList teamlist = doc.GetElementsByTagName("team");
                lfsteam team = null;
                foreach (XmlNode item in teamlist)
                {
                    XmlAttributeCollection att = item.Attributes;
                    //Console.WriteLine("Create Team with name " + att["name"].InnerText);
                    if (item.HasChildNodes)
                    {

                        XmlNodeList teammembers = item.ChildNodes;
                        //Console.WriteLine(att["name"].InnerText + " has " + teammembers.Count + " members");

                        team = new lfsteam(att["name"].InnerText, teammembers.Count);
                        

                        foreach (XmlNode member in teammembers)
                        {
                            XmlAttributeCollection memberatt = member.Attributes;

                            string[] xmldata = new string[5];
                            xmldata[0] = memberatt["username"].InnerText;
                            xmldata[1] = memberatt["realname"].InnerText;
                            xmldata[2] = memberatt["nationality"].InnerText;

                            team.MemberXMLInfo(xmldata);


                            team.AddMemberInfoFromXML(memberatt["username"].InnerText, memberatt["realname"].InnerText, memberatt["nationality"].InnerText);
                            //Console.WriteLine("Username " + memberatt["username"].InnerText);
                            //Console.WriteLine("realname " + memberatt["realname"].InnerText);
                            //Console.WriteLine("nationality " + memberatt["nationality"].InnerText);
                        }
                    }
                    _teamlist.Add(team.TeamName, team);

                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        public void Debug()
        {
            Console.WriteLine("Team List Information");
            Console.WriteLine(this._handler.Configuration.LFSHost + ":" + this._handler.Configuration.LFSHostPort);
            foreach (KeyValuePair<string, lfsteam> test in _teamlist)
            {
                lfsteam team = test.Value;
                team.Debug();
                
            }

        }


        public bool Connected { get { return _handler.State == InSimHandler.HandlerState.Connected;} }


        public int TotalRacers { get { return this._hashtable.Count; } }
    }
}
